using CommunityToolkit.Mvvm.ComponentModel;
using Sensor.UI.Models;
using System.Collections.Concurrent;
using System.Text;
using System.Windows.Threading;

namespace Sensor.UI.ViewModels;

/// <summary>
/// 系统日志视图模型
/// 负责管理系统日志的收集、格式化和显示
/// 使用生产者-消费者模式处理日志，确保UI响应性
/// </summary>
public partial class SystemLogViewModel : ViewModelBase
{
    #region 私有字段

    /// <summary>
    /// 后台日志队列（线程安全）
    /// 使用ConcurrentQueue确保多线程环境下的安全访问
    /// </summary>
    private readonly ConcurrentQueue<LogInfo> _logQueue = [];

    /// <summary>
    /// UI线程调度器
    /// 用于将后台线程的操作切换到UI线程执行
    /// </summary>
    private readonly Dispatcher _dispatcher;

    /// <summary>
    /// 最大日志行数限制
    /// 防止日志过多导致内存溢出
    /// </summary>
    private const int MaxLogLines = 10000;

    /// <summary>
    /// 当前日志行数计数器
    /// 用于跟踪当前显示的日志行数
    /// </summary>
    private int _currentLineCount;

    /// <summary>
    /// 日志刷新间隔（毫秒）
    /// 控制批量处理日志的频率
    /// </summary>
    private const int RefreshIntervalMs = 100;

    /// <summary>
    /// 日志保留比例
    /// 当达到最大行数时，保留此比例的日志
    /// </summary>
    private const double LogRetentionRatio = 0.8;

    /// <summary>
    /// 后台日志处理的取消令牌源
    /// 用于优雅地停止后台处理循环
    /// </summary>
    private readonly CancellationTokenSource _cancellationTokenSource = new();

    #endregion

    #region 公共属性

    /// <summary>
    /// 格式化后的日志文本
    /// 绑定到UI的日志显示控件
    /// 使用ObservableProperty特性自动实现属性变更通知
    /// </summary>
    [ObservableProperty]
    private string _formattedLogs = string.Empty;

    #endregion

    #region 事件

    /// <summary>
    /// 请求滚动到日志末尾事件
    /// 当有新日志添加时触发，通知UI滚动到最新位置
    /// </summary>
    public event EventHandler? ScrollToEndRequested;

    #endregion

    #region 构造函数

    /// <summary>
    /// 初始化SystemLogViewModel实例
    /// 设置UI线程调度器并启动日志处理循环
    /// </summary>
    public SystemLogViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
        _ = StartRefreshLoop(_cancellationTokenSource.Token);
    }

    #endregion

    #region 公共方法

    /// <summary>
    /// 添加新日志条目
    /// 线程安全方法，可从任何线程调用
    /// </summary>
    /// <param name="message">日志消息内容</param>
    /// <exception cref="ArgumentException">当消息为null或空时抛出</exception>
    public void AddLog(string message)
    {
        ArgumentException.ThrowIfNullOrEmpty(message);
        _logQueue.Enqueue(new LogInfo(message));
    }

    /// <summary>
    /// 清除所有日志命令（异步）
    /// 使用CommunityToolkit.Mvvm的RelayCommand特性自动生成命令
    /// 异步执行以避免阻塞UI线程
    /// </summary>
    /// <param name="cancellationToken">取消令牌，用于取消操作</param>
    /// <returns>表示异步操作的任务</returns>
    public async Task ClearLogsAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            cancellationToken.ThrowIfCancellationRequested();
            
            
            
            // 在UI线程上执行清空操作，确保AvalonEdit能够正确更新
            await _dispatcher.InvokeAsync(() =>
            {
                // 首先清空日志队列，防止后续处理时再次添加日志
                while (_logQueue.TryDequeue(out _))
                {
                    // 循环清空队列，不需要处理出队的元素
                }
                FormattedLogs = $"[{DateTime.UtcNow:HH:mm:ss.fff}] 清理数据";
                _currentLineCount = 0;

            }, DispatcherPriority.Normal, cancellationToken);
        }
        catch (OperationCanceledException)
        {
            // 操作被取消，这是预期的行为，不需要特殊处理
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"清除日志时发生错误: {ex.Message}");
        }
    }

    #endregion

    #region 私有方法

    /// <summary>
    /// 启动日志刷新循环
    /// 在后台线程中定期处理日志队列
    /// </summary>
    /// <param name="cancellationToken">取消令牌，用于停止循环</param>
    /// <returns>表示异步操作的任务</returns>
    private async Task StartRefreshLoop(CancellationToken cancellationToken)
    {
        try
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(RefreshIntervalMs, cancellationToken).ConfigureAwait(false);
                if (_logQueue.IsEmpty) continue;
                
                // 使用Task.Run包装Dispatcher.Invoke，以便可以使用ConfigureAwait
                await Task.Run(() => 
                {
                    _dispatcher.Invoke(() => ProcessLogQueue());
                }, cancellationToken).ConfigureAwait(false);
            }
        }
        catch (OperationCanceledException)
        {
            // 正常取消操作，不处理
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"日志刷新循环异常: {ex.Message}");
        }
    }

    /// <summary>
    /// 处理日志队列
    /// 从队列中批量取出日志并格式化为显示文本
    /// </summary>
    private void ProcessLogQueue()
    {
        var sb = new StringBuilder();
        int newLineCount = 0;

        while (_logQueue.TryDequeue(out var item))
        {
            sb.AppendLine($"[{item.Time:HH:mm:ss.fff}] {item.Message}");
            newLineCount++;
        }

        if (newLineCount == 0) return;

        _currentLineCount += newLineCount;
        if (_currentLineCount > MaxLogLines)
        {
            TrimOldLogs(sb.ToString(), newLineCount);
        }
        else
        {
            FormattedLogs += sb.ToString();
        }

        ScrollToEndRequested?.Invoke(this, EventArgs.Empty);
    }

    /// <summary>
    /// 修剪旧日志，保留指定比例的最新日志
    /// </summary>
    /// <param name="newLogText">新日志文本</param>
    /// <param name="newLineCount">新日志行数</param>
    private void TrimOldLogs(string newLogText, int newLineCount)
    {
        int keepLines = (int)(MaxLogLines * LogRetentionRatio);
        var lines = FormattedLogs.Split(['\n'], StringSplitOptions.RemoveEmptyEntries);

        if (lines.Length > keepLines)
        {
            int discardLines = lines.Length - keepLines;
            var recentLines = lines.Skip(discardLines).ToArray();
            FormattedLogs = string.Join("\n", recentLines) + "\n" + newLogText;
            _currentLineCount = keepLines + newLineCount;
        }
        else
        {
            FormattedLogs += "\n" + newLogText;
        }
    }

    #endregion

    #region 资源释放

    /// <summary>
    /// 释放资源
    /// 重写基类的Dispose方法，释放SystemLogViewModel特有的资源
    /// </summary>
    /// <param name="disposing">指示是否正在释放托管资源</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            try
            {
                _cancellationTokenSource.Cancel();
                _cancellationTokenSource.Dispose();
                // 清空日志队列
                while (_logQueue.TryDequeue(out _)) 
                { 
                    // 循环清空队列，不需要处理出队的元素
                }
                FormattedLogs = string.Empty;
                ScrollToEndRequested = null;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"释放资源时发生错误: {ex.Message}");
            }
        }
        
        // 调用基类的Dispose方法
        base.Dispose(disposing);
    }

    #endregion
}
